package com.zjj.learn.algorithmPlus.segment1.array;

import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.IntStream;

/**
 * 实现动态数组的功能
 *
 * @author zjj_admin
 */
public class DynamicArray implements Iterable<Integer> {

    /**
     * 数组的逻辑大小，有多少个数据
     */
    private int size = 0;

    /**
     * 容量
     */
    private int capacity = 8;

    /**
     * 初始化一个空数组，使用懒惰初始化的方式
     */
    private int[] array = {};


    /**
     * 在数组的最后面添加一个元素
     *
     * @param element
     */
    public void addLast(int element) {
        add(size, element);
    }

    /**
     * 在数组索引为 index 位置添加元素 element
     *
     * @param index   插入的索引位置
     * @param element 插入的元素
     */
    public void add(int index, int element) {
        //容量检查

        checkAndGrow();

        //检测索引
        checkIndex(index);
        //将原来的数组从索引为 index 的位置向后移动一位
        System.arraycopy(array, index, array, index + 1, size - index);
        //将添加元素放在索引为 index 的位置
        array[index] = element;
        size++;
    }

    /**
     * 数组扩容，将数组容量变为原来的 1.5 倍
     */
    private void checkAndGrow() {
        if (size == 0) {
            array = new int[capacity];
        }
        //当数据大小等于数组的容量时，就扩容
        if (size == capacity) {
            //变为原来的 1.5 倍
            capacity += capacity >> 1;
            int[] newArray = new int[capacity];
            //将数据进行拷贝
            System.arraycopy(array, 0, newArray, 0, size);
            //将新数组赋值给原始数组
            array = newArray;
        }
    }


    /**
     * 查询数组中索引为 index 的元素
     *
     * @param index
     * @return
     */
    public int get(int index) {
        checkIndex(index);
        return array[index];
    }

    /**
     * 删除索引为 index 的元素
     *
     * @param index 删除索引
     * @return 索引为 index 的元素
     */
    public int remove(int index) {
        int item = get(index);
        //将数组 index 索引后面的数据向前平移一位
        System.arraycopy(array, index + 1, array, index, size - index - 1);
        //将数组做后一位恢复成 0，并将数组大小减少 1
        array[size] = 0;
        size--;
        return item;
    }

    /**
     * 检测索引是否异常
     *
     * @param index 索引
     */
    public void checkIndex(int index) {
        //判断是否越界，index 不能小于零也不能大于了当前数组的大小；可以等于，等于数组的大小表示插在最后面
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("index 异常");
        }
    }


    /**
     * 遍历方法1：写一个遍历方法
     *
     * @param consumer 使用函数式接口
     */
    public void foreach(Consumer<Integer> consumer) {
        for (int i = 0; i < size; i++) {
            //将参数传递给 consumer，由调用方决定具体干什么
            consumer.accept(array[i]);
        }
    }

    /**
     * 遍历方法2：迭代遍历【实现 Iterable<Integer> 接口】
     *
     * @return
     */
    @Override
    public Iterator<Integer> iterator() {
        /**
         * 匿名内部类，和函数式编程相似
         */
        return new Iterator<Integer>() {
            int i = 0;

            /**
             * 有没有下一个数据
             * @return
             */
            @Override
            public boolean hasNext() {
                return i < size;
            }

            /**
             * 获取下一个数据
             * @return
             */
            @Override
            public Integer next() {
                return array[i++];
            }
        };
    }


    /**
     * 基于数据流遍历
     *
     * @return
     */
    public IntStream stream() {
        //Arrays.copyOfRange 拷贝是含头不含尾，从索引 0 开始，拷贝到索引 size - 1
        return IntStream.of(Arrays.copyOfRange(array, 0, size));
    }
}
